// Copyright (C) Calum Grant 2008

#include "cppscript"
#include "dynamic/extensions.hpp"
#include "wrap_cpp_container.hpp"

#include <cstdlib>


static_call( register_pickle_type( cmp_string, "" ) );
static_call( register_pickle_type( cmp_wstring, L"" ) );


namespace
{
	typedef std::basic_string<char, std::char_traits<char>, dynamic::allocator<char> > narrow_string;

	typedef std::basic_string<wchar_t, std::char_traits<wchar_t>, dynamic::allocator<wchar_t> > wide_string;

	template<typename Char> struct string_traits2;


	template<> struct string_traits2<char>
	{
		static std::string as_string(const var & v) 
		{
			return v.as_string(); 
		}

		static const var_cmp_index comparison_index = cmp_string;
	};


	template<> struct string_traits2<wchar_t>
	{
		static const var_cmp_index comparison_index = cmp_wstring;

		static dynamic::wstring as_string(const var & v) 
		{
			return v.as_wstring(); 
		}
	};


	template<typename Char>
	struct string_traits
	{
		typedef Char char_type;

		typedef Char value_type;

		typedef std::basic_string<Char> std_string;

		typedef std::basic_string<Char, std::char_traits<Char>, dynamic::allocator<Char> > container;

		static var key(int i) 
		{ 
			return i; 
		}

		static var value(Char i) 
		{
			return i; 
		}

		static var deref(Char i) 
		{ 
			return i; 
		}

		static void mark_reachable(int i, gc::garbage_collector & gc)
		{
		}

		static const var_cmp_index comparison_index = string_traits2<Char>::comparison_index;

		static char pad_value()
		{ 
			return ' ';
		}

		static char to_value(const var & v) 
		{
			return v.as_int();
		}

		static var back(const container & c)
		{ 
			return c[c.size()-1];
		}

		static void pop_back(container & c) 
		{
			c.resize(c.size()-1);
		}

		static var front(const container & c)
		{ 
			return c[0]; 
		}

		static void pickle(pickler & p, int ch)
		{
			p.write_int(ch);
		}

		static char_type unpickle(unpickler & u)
		{
			return u.read_int();
		}

		static std_string as_string(const var & s)
		{
			return string_traits2<Char>::as_string(s);
		}

		static const char * class_name() 
		{ 
			return "string";
		}

		static const char * iterator_class_name()
		{
			return "string::iterator";
		}
	};
}

namespace dynamic
{
	namespace types
	{

	/// Implements a string variable.
	/** \ingroup impl */
	template<typename Impl>
	class string_impl : public internal::wrap_vector_container<Impl>
	{
	public:
		string_impl(const typename Impl::char_type * s)
		{
			this->m_data = s;
		}

		string_impl(const typename Impl::container & s)
		{
			this->m_data = s;
		}

		template<typename Ch, typename Traits, typename Alloc>
		string_impl(const std::basic_string<Ch, Traits, Alloc> & s)
		{
			this->m_data.assign(s.begin(), s.end());
		}

		void short_output(ostream & os) 
		{
			output(os); 
		}

		void short_output(wostream & os) 
		{
			output(os); 
		}

		void output(ostream & os) 
		{
			for(typename Impl::container::const_iterator i=this->m_data.begin();
				i!=this->m_data.end(); 
				++i)
			{
				os << char(*i);
			}
		}

		var clone()
		{
			return new string_impl(*this);
		}

		void output(wostream & os) 
		{
			for(typename Impl::container::const_iterator i=this->m_data.begin();
				i!=this->m_data.end(); 
				++i)
			{
				os << wchar_t(*i);
			}
		}

		static int to_int(const narrow_string & str)
		{
			return atoi(str.c_str());
		}

		static int to_int(const wide_string & str)
		{
			/// \todo Yuck
			return atoi(std::string(str.begin(), str.end()).c_str());
		}

		static double to_double(const narrow_string & str)
		{
			return atof(str.c_str());
		}

		static double to_double(const wide_string & str)
		{
			/// \todo Yuck
			return atof(std::string(str.begin(), str.end()).c_str());
		}

		int as_int()
		{
			/// \todo Is this the right behaviour if the string is not an integer?
			return to_int(this->m_data);
		}

		double as_double()
		{
			/// \todo Is this the right behaviour if the string is not a number?
			return to_double(this->m_data);
		}

		var op_add(const var & s)
		{
			return this->m_data.c_str() + Impl::as_string(s);
		}

		var op_mul(const var & v)
		{
			typename Impl::std_string result;
			int n = v.as_int();
			result.reserve(n * this->m_data.size());
			for(int i=0; i<n; ++i)
				result.append(this->m_data.begin(), this->m_data.end()); // += *str;
			return result;
		}

		void assign_add(const var & v)
		{
			std::string v_str = v.as_string();
			this->m_data.append(v_str.begin(), v_str.end()); //  += Impl::as_string(v);
		}

		var_cmp_result compare2(const var & other)
		{
			string_impl * other_string = dynamic_cast<string_impl*>(other.impl().shared_var());

			if(!other_string) return cmp_not_equal;

			return this->m_data < other_string->m_data ? cmp_lt : this->m_data > other_string->m_data ? cmp_gt : cmp_equal;
		}
	};
}
}


var dynamic::internal::create_string(const char * str)
{
	return str ? new types::string_impl<string_traits<char> >(str) : var();
}

var dynamic::internal::create_string(const std::string & str)
{
	return new types::string_impl<string_traits<char> >(str);
}

var dynamic::internal::create_string(const wchar_t * str)
{
	return str ? new types::string_impl<string_traits<wchar_t> >(str) : var();
}

var dynamic::internal::create_string(const wstring & str)
{
	return new types::string_impl<string_traits<wchar_t> >(str);
}

var dynamic::string_from(const var & a)
{
	var result="";
	foreach(i, a) result.push_back(i);
	return result;
}

